﻿/*!	@file
	@brief 厳格っぷりをカスタマイズ可能な整数クラス
	       Integer class, which is static-type-checked strict and flexible at compile.

	@author kobake
	@date 2007.10.16
*/
/*
	Copyright (C) 2007, kobake
	Copyright (C) 2018-2022, Sakura Editor Organization

	SPDX-License-Identifier: Zlib
*/
#ifndef SAKURA_CSTRICTINTEGER_5B7614A0_282F_48F6_9420_CE672061CF3E_H_
#define SAKURA_CSTRICTINTEGER_5B7614A0_282F_48F6_9420_CE672061CF3E_H_
#pragma once

#include "primitive.h" // for Int

//int以外の整数型もintにキャストして扱う
#define STRICTINT_OTHER_TYPE_AS_INT(TYPE) \
	Me& operator += (TYPE rhs)			{ return operator += ((int)rhs); } \
	Me& operator -= (TYPE rhs)			{ return operator -= ((int)rhs); } \
	Me operator + (TYPE rhs)	const	{ return operator +  ((int)rhs); } \
	Me operator - (TYPE rhs)	const	{ return operator -  ((int)rhs); } \
	bool operator <  (TYPE rhs) const	{ return operator <  ((int)rhs); } \
	bool operator <= (TYPE rhs) const	{ return operator <= ((int)rhs); } \
	bool operator >  (TYPE rhs) const	{ return operator >  ((int)rhs); } \
	bool operator >= (TYPE rhs) const	{ return operator >= ((int)rhs); } \
	bool operator == (TYPE rhs) const	{ return operator == ((int)rhs); } \
	bool operator != (TYPE rhs) const	{ return operator != ((int)rhs); }

//! 暗黙の変換を許さない、整数クラス
template <
	int STRICT_ID,			//!< 型を分けるための数値。0 or 1。
	bool ALLOW_CMP_INT,		//!< intとの比較を許すかどうか
	bool ALLOW_ADDSUB_INT,	//!< intとの加減算を許すかどうか
	bool ALLOW_CAST_INT,	//!< intへの暗黙の変換を許すかどうか
	bool ALLOW_ASSIGNOP_INT	//!< intの代入を許すかどうか
>
class CStrictInteger{
private:
	typedef CStrictInteger<
		STRICT_ID,
		ALLOW_CMP_INT,
		ALLOW_ADDSUB_INT,
		ALLOW_CAST_INT,
		ALLOW_ASSIGNOP_INT
	> Me;
	static const int NOT_STRICT_ID = (1-STRICT_ID);

private:
	//!ゴミクラス
	class CDummy{
	public:
		CDummy();
		CDummy(int);
	};
	template<bool t, bool=false> struct ChooseIntOrDummy {
		typedef int Type;
	};
	// クラス内でテンプレートの特殊化をするとG++に怒られるので部分特殊化にする
	template<bool _> struct ChooseIntOrDummy<false, _> {
		typedef CDummy Type;
	};
public:
	//コンストラクタ・デストラクタ
	CStrictInteger(){ m_value=0; }
	CStrictInteger(const Me& rhs){ m_value=rhs.m_value; }
	~CStrictInteger() noexcept = default;

	//intからの変換は、「明示的に指定したときのみ」可能
	explicit CStrictInteger(int value){ m_value=value; }

	//算術演算子 (加算、減算は同クラス同士でしか許さない)
	Me& operator += (const Me& rhs)	{ m_value += rhs.m_value; return *this; }
	Me& operator -= (const Me& rhs)	{ m_value -= rhs.m_value; return *this; }
	Me& operator *= (int n)			{ m_value *= n; return *this; }
	Me& operator /= (int n)			{ m_value /= n; return *this; }
	Me& operator %= (int n)			{ m_value %= n; return *this; }
	Me& operator %= (const Me& rhs)	{ m_value %= rhs.m_value; return *this; }

	//算術演算子２ (加算、減算は同クラス同士でしか許さない)
	Me operator + (const Me& rhs)	const{ Me ret=*this; return ret+=rhs; }
	Me operator - (const Me& rhs)	const{ Me ret=*this; return ret-=rhs; }
	Me operator * (int n)			const{ Me ret=*this; return ret*=n; }
	Me operator / (int n)			const{ Me ret=*this; return ret/=n; }
	Me operator % (int n)			const{ Me ret=*this; return ret%=n; }
	Me operator % (const Me& rhs)	const{ Me ret=*this; return ret%=rhs; }

	//算術演算子３
	int operator ++ ()   { return ++m_value; }	//++c;
	int operator ++ (int){ return m_value++; }	//c++;
	int operator -- ()   { return --m_value; }	//--c;
	int operator -- (int){ return m_value--; }	//c--;

	//算術演算子４
	Me operator - () const{ return Me(-m_value); }

	//代入演算子
	Me& operator = (const Me& rhs){ m_value=rhs.m_value; return *this; }

	//比較演算子
	bool operator <  (const Me& rhs) const{ return m_value <  rhs.m_value; }
	bool operator <= (const Me& rhs) const{ return m_value <= rhs.m_value; }
	bool operator >  (const Me& rhs) const{ return m_value >  rhs.m_value; }
	bool operator >= (const Me& rhs) const{ return m_value >= rhs.m_value; }
	bool operator == (const Me& rhs) const{ return m_value == rhs.m_value; }
	bool operator != (const Me& rhs) const{ return m_value != rhs.m_value; }

	//関数
	int GetValue() const{ return m_value; }
	void SetValue(int n){ m_value=n; }

	//Int(CLaxInt)への変換は常に許す
	operator Int() const{ return Int(m_value); }

	//int以外の整数型もintにキャストして扱う
	STRICTINT_OTHER_TYPE_AS_INT(short)
	STRICTINT_OTHER_TYPE_AS_INT(size_t)
	STRICTINT_OTHER_TYPE_AS_INT(LONG)

	// -- -- -- -- 別種のCStrictIntegerとの演算は絶対許さん(やりたきゃintでも介してください) -- -- -- -- //
private:
	template <bool B0,bool B1,bool B2,bool B3> Me&  operator += (const CStrictInteger<NOT_STRICT_ID,B0,B1,B2,B3>&);
	template <bool B0,bool B1,bool B2,bool B3> Me&  operator -= (const CStrictInteger<NOT_STRICT_ID,B0,B1,B2,B3>&);
	template <bool B0,bool B1,bool B2,bool B3> Me   operator +  (const CStrictInteger<NOT_STRICT_ID,B0,B1,B2,B3>&) const;
	template <bool B0,bool B1,bool B2,bool B3> Me   operator -  (const CStrictInteger<NOT_STRICT_ID,B0,B1,B2,B3>&) const;
	template <bool B0,bool B1,bool B2,bool B3> Me&  operator =  (const CStrictInteger<NOT_STRICT_ID,B0,B1,B2,B3>&);
	template <bool B0,bool B1,bool B2,bool B3> bool operator <  (const CStrictInteger<NOT_STRICT_ID,B0,B1,B2,B3>&) const;
	template <bool B0,bool B1,bool B2,bool B3> bool operator <= (const CStrictInteger<NOT_STRICT_ID,B0,B1,B2,B3>&) const;
	template <bool B0,bool B1,bool B2,bool B3> bool operator >  (const CStrictInteger<NOT_STRICT_ID,B0,B1,B2,B3>&) const;
	template <bool B0,bool B1,bool B2,bool B3> bool operator >= (const CStrictInteger<NOT_STRICT_ID,B0,B1,B2,B3>&) const;
	template <bool B0,bool B1,bool B2,bool B3> bool operator == (const CStrictInteger<NOT_STRICT_ID,B0,B1,B2,B3>&) const;
	template <bool B0,bool B1,bool B2,bool B3> bool operator != (const CStrictInteger<NOT_STRICT_ID,B0,B1,B2,B3>&) const;

	// -- -- -- -- ALLOW_ADDSUB_INTがtrueの場合は、intとの加減算を許す -- -- -- -- //
private:
	typedef typename ChooseIntOrDummy< ALLOW_ADDSUB_INT>::Type AddsubIntegerType;
	typedef typename ChooseIntOrDummy<!ALLOW_ADDSUB_INT>::Type NotAddsubIntegerType;
public:
	Me& operator += (AddsubIntegerType rhs) { m_value += rhs; return *this; }
	Me& operator -= (AddsubIntegerType rhs) { m_value -= rhs; return *this; }
	Me  operator +  (AddsubIntegerType rhs) const{ Me ret=*this; return ret+=rhs; }
	Me  operator -  (AddsubIntegerType rhs) const{ Me ret=*this; return ret-=rhs; }
private:
	//※ALLOW_ADDSUB_INTがfalseの場合は、privateメンバを置くことにより、「明示的に」加減算を禁止する。
	Me& operator += (NotAddsubIntegerType rhs);
	Me& operator -= (NotAddsubIntegerType rhs);
	Me  operator +  (NotAddsubIntegerType rhs) const;
	Me  operator -  (NotAddsubIntegerType rhs) const;

	// -- -- -- -- ALLOW_CMP_INTがtrueの場合は、intとの比較を許す -- -- -- -- //
private:
	typedef typename ChooseIntOrDummy< ALLOW_CMP_INT>::Type CmpIntegerType;
	typedef typename ChooseIntOrDummy<!ALLOW_CMP_INT>::Type NotCmpIntegerType;
public:
	bool operator <  (CmpIntegerType rhs) const{ return m_value <  rhs; }
	bool operator <= (CmpIntegerType rhs) const{ return m_value <= rhs; }
	bool operator >  (CmpIntegerType rhs) const{ return m_value >  rhs; }
	bool operator >= (CmpIntegerType rhs) const{ return m_value >= rhs; }
	bool operator == (CmpIntegerType rhs) const{ return m_value == rhs; }
	bool operator != (CmpIntegerType rhs) const{ return m_value != rhs; }
private:
	//※ALLOW_CMP_INTがfalseの場合は、privateメンバを置くことにより、「明示的に」比較を禁止する。
	bool operator <  (NotCmpIntegerType rhs) const;
	bool operator <= (NotCmpIntegerType rhs) const;
	bool operator >  (NotCmpIntegerType rhs) const;
	bool operator >= (NotCmpIntegerType rhs) const;
	bool operator == (NotCmpIntegerType rhs) const;
	bool operator != (NotCmpIntegerType rhs) const;

	// -- -- -- -- ALLOW_CAST_INTがtrueの場合は、intへの暗黙の変換を許す -- -- -- -- //
private:
	typedef typename ChooseIntOrDummy< ALLOW_CAST_INT>::Type CastIntegerType;
	typedef typename ChooseIntOrDummy<!ALLOW_CAST_INT>::Type NotCastIntegerType;
public:
	operator CastIntegerType() const{ return m_value; }
private:
	//※ALLOW_CAST_INTがfalseの場合は、privateメンバを置くことにより、「明示的に」暗黙変換を禁止する。
	operator NotCastIntegerType() const;

	// -- -- -- -- ALLOW_ASSIGNOP_INTがtrueの場合は、intの代入を許す -- -- -- -- //
private:
	typedef typename ChooseIntOrDummy< ALLOW_ASSIGNOP_INT>::Type AssignIntegerType;
	typedef typename ChooseIntOrDummy<!ALLOW_ASSIGNOP_INT>::Type NotAssignIntegerType;
public:
	Me& operator = (const AssignIntegerType& rhs){ m_value = rhs; return *this; }
private:
	//※ALLOW_ASSIGNOP_INTがfalseの場合は、privateメンバを置くことにより、「明示的に」代入を禁止する。
	Me& operator = (const NotAssignIntegerType&);

	// -- -- -- -- メンバ変数 -- -- -- -- //
private:
	int m_value;
};

//左辺がint等の場合の演算子
#define STRICTINT_LEFT_INT_CMP(TYPE) \
	template <int N,bool B0,bool B1,bool B2,bool B3> inline bool operator <  (TYPE lhs, const CStrictInteger<N,B0,B1,B2,B3>& rhs){ return rhs> lhs; } \
	template <int N,bool B0,bool B1,bool B2,bool B3> inline bool operator <= (TYPE lhs, const CStrictInteger<N,B0,B1,B2,B3>& rhs){ return rhs>=lhs; } \
	template <int N,bool B0,bool B1,bool B2,bool B3> inline bool operator >  (TYPE lhs, const CStrictInteger<N,B0,B1,B2,B3>& rhs){ return rhs< lhs; } \
	template <int N,bool B0,bool B1,bool B2,bool B3> inline bool operator >= (TYPE lhs, const CStrictInteger<N,B0,B1,B2,B3>& rhs){ return rhs<=lhs; } \
	template <int N,bool B0,bool B1,bool B2,bool B3> inline bool operator == (TYPE lhs, const CStrictInteger<N,B0,B1,B2,B3>& rhs){ return rhs==lhs; } \
	template <int N,bool B0,bool B1,bool B2,bool B3> inline bool operator != (TYPE lhs, const CStrictInteger<N,B0,B1,B2,B3>& rhs){ return rhs!=lhs; } \
	template <int N,bool B0,bool B1,bool B2,bool B3> inline CStrictInteger<N,B0,B1,B2,B3> operator + (TYPE lhs, const CStrictInteger<N,B0,B1,B2,B3>& rhs){ return  rhs+lhs; } \
	template <int N,bool B0,bool B1,bool B2,bool B3> inline CStrictInteger<N,B0,B1,B2,B3> operator - (TYPE lhs, const CStrictInteger<N,B0,B1,B2,B3>& rhs){ return -rhs+lhs; } \
	template <int N,bool B0,bool B1,bool B2,bool B3> inline CStrictInteger<N,B0,B1,B2,B3> operator * (TYPE lhs, const CStrictInteger<N,B0,B1,B2,B3>& rhs){ return  rhs*lhs; }
STRICTINT_LEFT_INT_CMP(int)
STRICTINT_LEFT_INT_CMP(short)
STRICTINT_LEFT_INT_CMP(size_t)
STRICTINT_LEFT_INT_CMP(LONG)
#endif /* SAKURA_CSTRICTINTEGER_5B7614A0_282F_48F6_9420_CE672061CF3E_H_ */
